#include "DeflatorSource.h"
#include "log/log.h"

#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
#	include <fcntl.h>
#	include <io.h>
#	define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
#	define SET_BINARY_MODE(file)
#endif

namespace phnq
{
	namespace kit
	{
		namespace compression
		{
			static phnq::kit::log::Logger *log = phnq::kit::log::Logger::get("phnq::kit::compression::DeflatorSource");
			
			DeflatorSource::DeflatorSource(istream *is, int level, int chunkSize) : boost::iostreams::source()
			{
				this->is = is;
				this->chunkSize = chunkSize;
				this->in = (unsigned char*)malloc(chunkSize * sizeof(unsigned char));
				
				strm.zalloc = Z_NULL;
				strm.zfree = Z_NULL;
				strm.opaque = Z_NULL;

				ret = deflateInit(&strm, level);

				if(ret != Z_OK)
					log->error("deflateInit: %i", ret);
			}
			
			streamsize DeflatorSource::read(char* s, streamsize n)
			{
				if(!in)
					return -1;
					
				is->read((char*)in, n);
				
				n = is->gcount();
				
				strm.avail_in = n;
				strm.next_in = in;

				flush = (is->eof() ? Z_FINISH : Z_SYNC_FLUSH);
				
				int len = 0;
				do
				{
					strm.avail_out = chunkSize;
					strm.next_out = (unsigned char*)s;
					
					ret = deflate(&strm, flush);
					
					if(ret == Z_STREAM_ERROR)
						log->error("deflate error: %i", ret);
					
					have = chunkSize - strm.avail_out;
					
					len += have;
				} while (strm.avail_out == 0);
				
				if(flush == Z_FINISH)
				{
					deflateEnd(&strm);
					free(in);
					in = NULL;
					delete is;
					is = NULL;
				}
				
				return len;
			}
		}
	}
}
